﻿using System;
using System.Drawing;
using System.Windows.Forms;
// Некоторая библиотека, которая включает в себя тип FastBitmap
//using Image;
using System.Threading;
using System.Collections.Generic;

// (Это можно добавить в описание проекта)
// Описание задачи: 
//      Дано изображение. 
//      Требуется приблизить его равносторонними треугольниками, квадратами, кругами
//      разных цветов и размеров с помощью генетических алгоритмов.

// Polygon - любая фигура (равносторонний треугольник, квадрат, круг)

namespace GeneticAlgorithm
{
    public partial class MainForm : Form
    {
        // Переменная для проверки: запущенно ли вычисление
        private bool isRunning = false;

        // Поток, в котором происходят все основные вычисления.
        private Thread thread;

        //Номер поколения
        private int Generation;

        //Текущая популяция
        private List<Tuple<Workarea, double>> Population;

        // Матрица цветов пикселей загруженного изображения
        Color[,] sourceColors;

        //Номер изображения для печати
        private int number;

        //Номер итерации
        private int step;

        //Путь к созраненным файлам
        private string path;

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainShown(object sender, EventArgs e)
        {
            Bitmap pict = new Bitmap(320, 240);

            using (Graphics g = Graphics.FromImage(pict))
            {
                g.Clear(Color.White);

                // Подсказка
                g.DrawString("Кликните для выбора файла", new Font("Arial", 12), Brushes.Black, new PointF(35, 100));
            }
            pictureBox.Image = pict;


        }

        private void StartTest()
        {
            Bitmap sourceImg = new Bitmap("C:\\Users\\Albina\\Pictures\\Gen3.jpg");
            path = "D:\\NewGen1";
            pictureBox.Image = sourceImg;
            Constants.Width = sourceImg.Width;
            Constants.Height = sourceImg.Height;
            sourceColors = new Color[sourceImg.Width, sourceImg.Height];
            for (int i = 0; i < sourceImg.Width; i++)
            {
                for (int j = 0; j < sourceImg.Height; j++)
                {
                    sourceColors[i, j] = sourceImg.GetPixel(i, j);
                }
            }
            Population = null;
            button.Enabled = true;            
        }

        // Действия при клике по PictureBox
        private void PictureBox_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            // Bitmap загруженного изображения
            Bitmap sourceImg;

            // Определяем местоположение изображения
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                // Проверяем, что файл существует
                try
                {
                    // Создаём Bitmap
                    sourceImg = new Bitmap(openFileDialog1.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    return;
                }

                if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                {
                    path = folderBrowserDialog1.SelectedPath;
                }

                // Загружаем изображение в форму
                pictureBox.Image = sourceImg;

                // Запоминаем размеры изображения
                Constants.Width = sourceImg.Width;
                Constants.Height = sourceImg.Height;

                // Создаём массив цветов пикселей изображения
                sourceColors = new Color[sourceImg.Width, sourceImg.Height];

                // Заполняем массив цветов пикселей
                for (int i = 0; i < sourceImg.Width; i++)
                {
                    for (int j = 0; j < sourceImg.Height; j++)
                    {
                        sourceColors[i, j] = sourceImg.GetPixel(i, j);
                    }
                }

                Population = null;
                //fitness = double.MaxValue;

                button.Enabled = true;
            }
        }

        // Действия при нажатии кнопки Старт\Стоп
        private void button_Click(object sender, EventArgs e)
        {
            // Проверяем запущено ли вычисление
            if (isRunning)
            {
                // Останавливаем вычисления и "убиваем" соответствующий им поток
                button.Text = "Старт";
                //StatusLabel.Text = fitness.ToString();
                isRunning = false;
                KillThread();
                this.pictureBox.Hide();
                this.workareaInformation.Text = GetInformation();
                this.workareaInformation.Show();
                button.Enabled = false;
            }
            else
            {
                // Запускаем вычисления
                isRunning = true;
                button.Text = "Стоп";
                thread = new Thread(new ThreadStart(Start));                
                thread.IsBackground = true;
                thread.Start();
                
            }
        }

        // Завершение потока с вычислениями
        private void KillThread()
        {
            if (thread != null)
            {
                thread.Abort();
            }
        }

        // Запуск вычислений
        private void Start()
        {

            // Если текущее приближение равно null, тогда определяем его случайным образом
            if (Population == null)
            {
                Generation = 1;
                Population = SetRundomPopulation();
                SortByFitness();                
            }

            while (Generation <= Constants.MaxGeneration)
            {
                Generation++;
                Population = GetNextPopulation();

                SortByFitness();

                if (Generation % 50 == 0)
                {
                    Save(Population[0].Item1);
                }
            }
        }

        private List<Tuple<Workarea, double>> GetNextPopulation()
        {
            List<Tuple<Workarea, double>> newPopulation = new List<Tuple<Workarea, double>>();

            Workarea area1;
            Workarea area2;

            newPopulation.Add(Population[0]);

            for (int i = 0; i < 3; i++)
            {
                for (int j = i + 1; j < 5; j++)
                {
                    lock (Population[i].Item1)
                    {
                        area1 = Population[i].Item1.Clone() as Workarea;
                    }
                    lock (Population[j].Item1)
                    {
                        area2 = Population[j].Item1.Clone() as Workarea;
                    }
                    Workarea newWorkarea = Crossover(area1, area2);
                    newWorkarea.Mutate();
                    newPopulation.Add(new Tuple<Workarea, double>(newWorkarea, GetFitness(newWorkarea)));
                }
            }            
            return newPopulation;
        }
       
        private Workarea Crossover(Workarea area1, Workarea area2)
        {
            Workarea newArea = new Workarea();
            newArea.Polygons = new List<Polygon>();

            int length = Constants.Rand.Next(Math.Min(area1.Polygons.Count, area2.Polygons.Count), Math.Max(area1.Polygons.Count, area2.Polygons.Count)+1);

            for (int i = 0; i < Math.Min(area1.Polygons.Count, area2.Polygons.Count); i++)
            {
                if (Constants.Rand.NextDouble() <= 0.5)
                {
                    newArea.Polygons.Add(area1.Polygons[i]);
                }
                else
                {
                    newArea.Polygons.Add(area2.Polygons[i]);
                }
            }
            for (int i = Math.Min(area1.Polygons.Count, area2.Polygons.Count); i < length; i++)
            {
                if (area1.Polygons.Count < area2.Polygons.Count)
                {
                    newArea.Polygons.Add(area2.Polygons[i]);
                }
                else
                {
                    newArea.Polygons.Add(area1.Polygons[i]);
                }
            }

            return newArea;
        }

        private void SortByFitness()
        {
            Population.Sort(CompareByFitness);
        }

        private static int CompareByFitness(Tuple<Workarea, double> a, Tuple<Workarea, double> b)
        {
            return a.Item2.CompareTo(b.Item2);
        }

        private double GetFitness(Workarea workarea)
        {
            return workarea.Fitness(sourceColors);
        }

        private List<Tuple<Workarea, double>> SetRundomPopulation()
        {
            List<Tuple<Workarea, double>> population = new List<Tuple<Workarea, double>>();
            for (int i = 0; i < Constants.Population; i++)
            {
                Workarea newWorkarea = new Workarea();
                newWorkarea.SetRandom();
                population.Add(new Tuple<Workarea, double>(newWorkarea, GetFitness(newWorkarea)));
            }
            return population;
        }

        //Сохранение картинки
        private void Save(Workarea workarea)
        {
            Bitmap img = workarea.Draw();
            try
            {
                img.Save(path + "\\" + (Generation).ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
            img.Dispose();
        }

        private string GetInformation()
        {
            return "Вычисления закончены";            
        }
    }
}